#include "input.hpp"

static const char *CMDSTRS[Input::ENTER] =
{
	"left",
	"right",
	"up",
	"down",
	"attack",
	"jump"
};

#include "data.hpp"
#include <string>
static std::map<std::string, int> strkeys;

bool Input::Open()
{
	binding = NOTBINDING;
	bindcmd = -1;

	SDLKey keys[COMMANDCT] =
	{
		SDLK_LEFT,
		SDLK_RIGHT,
		SDLK_UP,
		SDLK_DOWN,
		SDLK_x,
		SDLK_z,
		SDLK_RETURN,
		SDLK_ESCAPE
	};

	for (int k = SDLK_FIRST; k < SDLK_LAST; ++k)
	{
		if (KeyStr((SDLKey)k))
		{
			strkeys[std::string(KeyStr((SDLKey)k))] = k;
		}
	}

	Data cfg;
	if (cfg.Open("input"))
	{
		for (Data d = cfg.Begin(); d; ++d)
		{
			std::string name;
			for (int i = 0; i < ENTER; ++i)
			{
				name = "key_";
				name += CMDSTRS[i];
				if (d.Named(name.c_str()))
				{
					std::string keystr (d.CStr());
					std::map<std::string,int>::iterator it;
					it = strkeys.find(keystr);
					if (it != strkeys.end())
						keys[i] = (SDLKey)it->second;
				}
			}
		}
		cfg.Close();
	}

	cmdkeys.resize(COMMANDCT);
	cmdtimes.resize(COMMANDCT, -1);

	for (int i = 0; i < COMMANDCT; ++i)
	{
		KeyBind(keys[i], i);
		if (i >= ENTER)
			reservedkeys.insert(keys[i]);
		if (i >= ATTACK)
			JoyBind(i - ATTACK, i);
	}

	for (int i = 0; i < SDL_NumJoysticks(); ++i)
	{
		SDL_Joystick *joy = SDL_JoystickOpen(i);
		if (joy)
			joysticks.push_back(joy);
	}

	return true;
}

bool Input::Save()
{
	Data cfg;
	cfg.InitObj();
	std::string name;
	Data prev;
	for (int i = 0; i < ENTER; ++i)
	{
		name = "key_";
		name += CMDSTRS[i];
		prev = cfg.InsAfter(prev, name.c_str(),
				Data(KeyStr(cmdkeys[i])));
	}
	bool saved = cfg.Save("input");
	cfg.Close();
	return saved;
}

void Input::Close()
{
	keycmds.clear();
	cmdkeys.clear();
	reservedkeys.clear();
	jbcmds.clear();

	for (unsigned i = 0; i < joysticks.size(); ++i)
	{
		SDL_JoystickClose(joysticks[i]);
	}

	joysticks.clear();
}

void Input::Event(const SDL_Event & event)
{
	std::map<SDLKey, int>::iterator keycmd;
	std::map<int, int>::iterator jbcmd;
	int dir;

	const Sint16 JOYDEAD = 1024;

	switch (event.type)
	{
	case SDL_KEYDOWN:
		if (binding == KEYBINDING)
		{
			binding = NOTBINDING;
			bindstate = KeyBind(event.key.keysym.sym, bindcmd);
			break;
		}
		keycmd = keycmds.find(event.key.keysym.sym);
		if (keycmd == keycmds.end())
			break;

		CmdStart(keycmd->second);
		break;

	case SDL_KEYUP:
		keycmd = keycmds.find(event.key.keysym.sym);
		if (keycmd == keycmds.end())
			break;

		if (CmdTime(keycmd->second) > 0)
			CmdEnd(keycmd->second);
		break;

	case SDL_JOYAXISMOTION:
		if (event.jaxis.axis >= 2)
			break;

		dir = event.jaxis.axis * 2;
		if (event.jaxis.value < -JOYDEAD)
			CmdStart(dir);
		else
			CmdEnd(dir);

		++dir;
		if (event.jaxis.value > JOYDEAD)
			CmdStart(dir);
		else
			CmdEnd(dir);
		break;

	case SDL_JOYBUTTONDOWN:
		if (binding == JOYBINDING)
		{
			binding = NOTBINDING;
			bindstate = JoyBind(event.jbutton.button, bindcmd);
			break;
		}
	case SDL_JOYBUTTONUP:	// || SDL_JOYBUTTONDOWN
		jbcmd = jbcmds.find(event.jbutton.button);
		if (jbcmd == jbcmds.end())
			break;

		if (event.jbutton.state == SDL_PRESSED)
			CmdStart(jbcmd->second);
		else if (CmdTime(jbcmd->second) > 0)
			CmdEnd(jbcmd->second);
		break;
	
	default: 
		;
	}
}

void Input::Update()
{
	for (int i = 0; i < COMMANDCT; ++i)
	{
		if (CmdOn(i))
			++cmdtimes[i];
		if (CmdTime(i) == 1)
			edgebits &= ~CmdBit(i);
	}
}

int Input::KeyBind(SDLKey key, int cmd)
{
	if (reservedkeys.find(key) != reservedkeys.end())
		return BINDFAIL;

	SDLKey othkey = cmdkeys[cmd];
	int othcmd = NOCOMMAND;
	
	std::map<SDLKey, int>::iterator kc = keycmds.find(key);
	if (kc != keycmds.end())
	{
		othcmd = kc->second;
	}

	kc = keycmds.find(othkey);
	if (kc != keycmds.end())
	{
		keycmds.erase(kc);
	}

	keycmds[key] = cmd;
	cmdkeys[cmd] = key;
	if (othcmd > NOCOMMAND)
	{
		keycmds[othkey] = othcmd;
		cmdkeys[othcmd] = othkey;
	}
	
	return BINDOK;
}

int Input::JoyBind(int jb, int cmd)
{
	for (std::map<int, int>::iterator it = jbcmds.begin();
			it != jbcmds.end();
			)
	{
		if (it->second == cmd)
			jbcmds.erase(it++);
		else
			++it;
	}

	jbcmds[jb] = cmd;
	return BINDOK;
}

const wchar_t* Input::CmdWStr(int cmd)
{
	static const wchar_t *CMDWSTRS[] =
	{
		L"LEFT    ",
		L"RIGHT   ",
		L"UP      ",
		L"DOWN    ",
		L"ATTACK  ",
		L"JUMP    "
	};

	return CMDWSTRS[cmd];
}

void Input::KeyRepeat(bool rpt)
{
	SDL_EnableKeyRepeat(rpt ? SDL_DEFAULT_REPEAT_DELAY : 0,
			SDL_DEFAULT_REPEAT_INTERVAL);
}

Input Inp;
